#include "libvp.hpp"

std::string VPFile::getSizeStr()
{
  std::stringstream strm;
  if (size < 1024)
    {
      strm << size << " B";
    }
  else
    {
      if (size < 1024*1024)
      {
        // static cast rounds down
        strm << static_cast<int>(.5 + size/1024.) << " KB";
      }
      else
        {
          strm << static_cast<int>(.5 + size/(1024*1024)) << " MB";
        }
    }
  return strm.str();
}

std::string VPDir::getNameWithCount()
{
  /***
      Returns directory name of the form "<dirname> (<filecount>)"
      For mirroring VPView32 behaviour.
  ***/
  std::stringstream strm;
  strm << name << " (" << files.size() << ")";
  return strm.str();
}

VP::VP(std::string infilepath)
{
  open(infilepath);
}

void VP::close()
{
  if (inFile.is_open())
    {
      inFile.close();
    }
  diroffset = -1;
  direntries = -1;
  datadir = VPDir();
}

void VP::open(std::string infilepath)
{
  //Precaution
  close();

  /*********************************************************************
Open file, do some basic error checking
  *********************************************************************/

  inFile.open(infilepath.c_str(), std::ifstream::in | std::ifstream::binary);

  if (!inFile.is_open())
    {
      throw std::string("Failed to open " + infilepath);
    }
  
  //Some basic error checking
  char parsestr [5];
  int parseint;
  
  inFile.read(parsestr, 4);
  parsestr[4] = '\0'; // manual terminating character
 
  if (strcmp(parsestr, "VPVP") != 0)
    {
      throw std::string("Header doesn't start with VPVP - found '" + std::string(parsestr) + "'");
      return;
    }
  
  inFile.read((char*) &parseint, 4);
  if (parseint != 2)
    {
      throw std::string("Invalid VP version in header");
      return;
    }

  //Read file index offset, number of entries
  inFile.read((char*) &diroffset, 4);
  inFile.read((char*) &direntries, 4);

  /*********************************************************************
Construct directory tree
  *********************************************************************/

  // Start with 'data' node
  inFile.seekg(diroffset, std::ios_base::beg);
  TreeEntry newElement (parseElement());

  if (strcasecmp(newElement.getName().c_str(), "data") != 0)
    {
      // Well-formed VPs should ALWAYS start with /data
      throw std::string("Root node not named 'data'");
    }
  datadir.fromEntry(newElement);
  parseChildren(&datadir);

  /***************************************************************
Opening (hopefully) successful
  ****************************************************************/
  return;
};

std::vector<char> VP::exportFile(VPFile in_storethis)
{
  //Allocate memory to store file contents
  std::vector<char> ret (in_storethis.getSize());

  // Find & read file contents
  inFile.seekg(in_storethis.getOffset(), std::ios_base::beg);
  inFile.read(&ret[0], in_storethis.getSize());
  
  return ret;
}

void VP::parseChildren(VPDir* dir)
{
  do
    {
      TreeEntry newElement (parseElement());

      if (!newElement.isBackDir())
        {
          if (newElement.isFile())
            {
              dir->addFile(VPFile(newElement));
            }
          else
            {
              // Directories filled by recursion. This will likely
              // mess up if not all dirs are backdir-ed at the end.
              VPDir newdir (newElement);
              parseChildren(&newdir);
              dir->addSubdir(newdir);
            }
        }
      else
        {
          return;
        }
    }
  while (inFile.good());
};

TreeEntry VP::parseElement()
{
  // Read from current cursor position
  // !!! Handle with care !!!

  // In order of appearance:
  int entryoffset;
  int entrysize;
  char entryname[32];
  int entrytimestamp;

  inFile.read((char*) &entryoffset, 4);
  inFile.read((char*) &entrysize, 4);
  inFile.read(entryname, 32);
  inFile.read((char*) &entrytimestamp, 4);

  // Create TreeEntry
  TreeEntry newelm (entryoffset, entrysize, 
                    std::string(entryname), entrytimestamp);

  // Return
  return newelm;
}

